#ifndef __CE_GFX_LEVELOFDETAIL_H__
#define __CE_GFX_LEVELOFDETAIL_H__

#include <assert.h>
#include <asset/asset.h>
#include <stdio.h>
#include <vector>
#include <iobject.h>
#include <math.h>

/**
 * \addtogroup graphics
 */


/**
 * @brief Interface for controling what level of detail to chose.
 */
struct iLevelOfDetailSolver : public ceAsset
{
  CE_ASSET_ID4('l', 's', 'l', 'v')

  iLevelOfDetailSolver () : CE_ASSET_SUPER () { }
  virtual unsigned Solve (unsigned numStages, float distance, float size) const = 0;
};

/**
 * @brief Default implementation of linear interpolating over the levels.
 */
class ceLinearLevelOfDetailSolver : public iLevelOfDetailSolver
{
  CE_OBJECT
private:
  float _min;
  float _max;
  bool  _getLast;

public:

  ceLinearLevelOfDetailSolver (float min, float max, bool getLast)
    : iLevelOfDetailSolver ()
    , _min (min)
    , _max (max)
    , _getLast (getLast)
  {
    CE_OBJECT_CONSTR;
  }

  void SetMin (float min) { _min = min; }
  float GetMin () const { return _min; }

  void SetMax (float max) { _max = max; }
  float GetMax () const { return _max; }

  void SetGetLast (bool getLast) { _getLast = getLast; }
  bool IsGetLast () const { return _getLast; }

  virtual unsigned Solve(unsigned numStages, float distance, float size) const
  {
    if (distance < 0e-6f)
      {
        return 0;
      }



    float a = atan (size / distance);
    a = a < 0.0 ? -a : a;

    float d = (a - _min) / (_max - _min);
    if (d < 0.0f)
      {
        d = 0.0f;
      }
    unsigned level = (unsigned)(d * (float)(numStages-1));
    if (level >= numStages && _getLast)
      {
        level = numStages - 1;
      }
    return level;
  }
};


/**
 * \brief Generic class for level of detail handling.
 */
template<typename T>
class ceLevelOfDetail : public iObject
{
	CE_OBJECT
private:
  std::vector<T*>             _levels;
  iLevelOfDetailSolver*  _solver;

public:
  ceLevelOfDetail (iLevelOfDetailSolver* solver = 0)
    : _solver (0)
  {
		CE_OBJECT_CONSTR
    SetSolver(solver);
    _levels.clear();
  }

  ~ceLevelOfDetail ()
  {
    CE_UNSET(_solver);
    Clear ();
  }

  void Clear ()
  {
    for (unsigned i=0; i<_levels.size(); i++)
      {
        CE_UNSET(_levels[i]);
      }
    _levels.clear();
  }

  void SetSolver (iLevelOfDetailSolver* solver)
  {
    CE_SET(_solver, solver);
  }

  void Add (T* t)
  {
    if (!t)
      {
        return;
      }
    _levels.push_back(t);
    t->AddRef ();
  }

  bool Remove (T* t)
  {
    if (!t)
      {
        return false;
      }


    for (typename std::vector<T*>::iterator it = _levels.begin();
         it != _levels.end();
         ++it)
      {
        if ((*it) == t)
          {
            _levels.erase(it);
            t->Release ();
            return true;
          }
      }

    return false;
  }

  unsigned GetNumberOfLevels () const
  {
    return _levels.size();
  }

  const T* Get (unsigned level = 0) const
  {
    if (level >= _levels.size ())
      {
        return 0;
      }
    return _levels[level];
  }

  T* Get (unsigned level = 0)
  {
    if (level >= _levels.size ())
      {
        return 0;
      }
    return _levels[level];
  }


  const T* Eval (float distance, float size) const
  {
    if (_solver)
      {
        return Get(_solver->Solve(_levels.size (), distance, size));
      }
    else
      {
        return Get(0);
      }
  }


  T* Eval (float distance, float size)
  {
    if (_solver)
      {
        return Get(_solver->Solve(_levels.size (), distance, size));
      }
    else
      {
        return Get(0);
      }
  }



};

/** @} */


#endif /* ! __CE_GFX_LEVELOFDETAIL_H__ */
